package com.yichuang.security.javajwt.main.config;

import com.yichuang.security.common.OAuth2Client;
import com.yichuang.security.javajwt.main.service.MyUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

/**
 * <p> oauth2认证服务器配置 </p>
 *
 * @author tuonioooo
 * @site https://miliqkdoc.motopa.cn/
 * @date 2018/8/22 14:20
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    // token有效期24小时，单位为秒 24 * 60 * 60
    private static final int accessTokenValiditySeconds = 24 * 60 * 60;
    private final AuthenticationManager authenticationManager;
    private final MyUserDetailsService myUserDetailsService;
    private final CustomTokenEnhancer customTokenEnhancer;

    public AuthorizationServerConfiguration(AuthenticationManager authenticationManager, MyUserDetailsService myUserDetailsService, CustomTokenEnhancer customTokenEnhancer) {
        this.authenticationManager = authenticationManager;
        this.myUserDetailsService = myUserDetailsService;
        this.customTokenEnhancer = customTokenEnhancer;
    }

    /**
     * 作用：配置客户端详细信息服务。
     * 用途：定义哪些客户端可以访问授权服务器，以及它们的权限和范围。可以使用内存/JDBC或自定义的方式来存储客户端信息。
     * 常用配置：
     * - clients.inMemory()：在内存中配置客户端。
     * - clients.jdbc(dataSource)：使用JDBC数据源来存储客户端信息。
     * - clients.withClient("client-id")：定义一个客户端ID。
     * - authorizedGrantTypes("authorization_code", "refresh_token")：定义客户端支持的授权类型。
     *
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // 使用内存存储客户端信息
                .withClient(OAuth2Client.CLIENT_ID) // 客户端ID
                .secret(OAuth2Client.CLIENT_SECRET) // 客户端密钥  如果定义了编码这里需要自动编码存储的，本示例采用不加密密码 所以没有编码
                .authorizedGrantTypes("password", "refresh_token", "authorization_code", "client_credentials") // 支持的授权模式
                .scopes("read", "write") // 授权范围
                .accessTokenValiditySeconds(accessTokenValiditySeconds) // token有效期
                .refreshTokenValiditySeconds(accessTokenValiditySeconds * 2); // refresh_token有效期
    }

    /**
     * 作用：配置授权和令牌端点及其服务。
     * 用途：定义授权服务器的端点、令牌存储、令牌增强、用户批准和授权等。
     * 常用配置：
     * - endpoints.tokenStore(tokenStore())：设置令牌存储策略，比如内存存储、JDBC存储等。
     * - endpoints.userDetailsService(userDetailsService)：设置用户详细信息服务。
     * - endpoints.authenticationManager(authenticationManager)：设置认证管理器。
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 配置认证管理器和UserDetailsService
        endpoints.authenticationManager(authenticationManager);
        endpoints.userDetailsService(myUserDetailsService);

        // 自定义Token生成方式
        endpoints.tokenEnhancer(customTokenEnhancer);

        // 指定Token存储方式为内存存储
        endpoints.tokenStore(tokenStore());
        // 设置授权服务器异常转换器
        endpoints.exceptionTranslator(new Auth2ResponseExceptionTranslator());
    }

    /**
     * 作用：配置授权服务器的安全性。
     * 用途：定义令牌端点的安全约束。你可以在这里设置哪些用户可以访问令牌端点，是否允许客户端直接访问令牌端点等。
     * 常用配置：
     * - tokenKeyAccess("permitAll()")：允许所有人访问令牌密钥。
     * - checkTokenAccess("isAuthenticated()")：要求用户认证后才能访问检查令牌的端点。
     *
     * @param oauthServer
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer.tokenKeyAccess("permitAll()")// 开启/oauth/token_key验证端口无权限访问
                .checkTokenAccess("isAuthenticated()")// 开启/oauth/check_token验证端口认证权限访问
                .allowFormAuthenticationForClients();//允许表单认证
    }

    /**
     * 指定token存储的位置为内存
     */
    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore(); // 使用内存存储Token
    }

}
